/**
 * 内置类型
 * 自定义验证方法 使用了自定义正则就不要在同时使用内置正则 没有去考虑两种同时使用的情况
 * @param {string} 						key
 * @param {any}  						value    	结果值
 * custom可返回Promise<resultType> | resultType
 * @param {Function}    				custom    	自定义验证方法
 * @param {string}						required  	是否必填
 * @param {string | Array<string>}		customReg
 * @param {string | Array<string>}		reg   		正则来源
 * @param {string | Array<string>}		msg   		错误提示
 */
import { Role, resultType, allResult, RoleMapItem } from "./interFaceType";

import { RegExpMap } from "./reg";

function isArrayNormal<T extends any[]>(value: T): T;
function isArrayNormal<T>(value: T): T[];
function isArrayNormal(value: any): any {
  return value instanceof Array ? value : [value];
}

/**
 * @example: 这里已不用在写返回类型,已经根据泛型和isArrayNormal自动推断
 */
const normalizingRole = {
  customReg<T extends Role["customReg"]>(key: T) {
    return isArrayNormal(key);
  },
  reg(key: keyof typeof RegExpMap) {
    const regArr = isArrayNormal(key);
    for (let i = 0, length = regArr.length; i < length; i++) {
      const regName = regArr[i].split("|")[0];
      if (!RegExpMap[regName]) {
        throw new Error(`内置正则中不存在${regArr[i]}此正则.`);
      }
    }
    return regArr;
  },
  msg<T extends Role["msg"]>(key: T) {
    return isArrayNormal(key);
  },
};

const validateMap = {
  //并没有判断是否是字面量对象 字面量对象里面是否是空 或者是map类型 map是空
  required(value: any) {
    if (value === "") return true;
    else if (value === null || value === undefined) return true;
    else if (value.length && value.length === 0) return true;
    else return false;
  },
};

export class Validate {
  roleMap: Map<string, RoleMapItem> = new Map();

  constructor(formRole: Array<Role>) {
    formRole.forEach((roleValue) => {
      const value: Role = Object.assign({}, roleValue);
      Object.keys(roleValue).forEach((rolekey) => {
        const isNormali = normalizingRole[rolekey];
        if (isNormali) value[rolekey] = isNormali(value[rolekey]);
      });
      if (value.customReg && value.reg) {
        // eslint-disable-next-line no-console
        console.warn(
          `${value.key}同时使用了内置正则和自定义正则\n 将会出现msg提示错误问题\n 可以获取暴露的内置验证方法在自定义方法中判断`
        );
      }
      this.roleMap.set(value.key, value as RoleMapItem);
    });
  }

  /**
   * 这里其实是可能传入错误的key是获取不到的 但是key的类型和返回暂因map
   * 不知道如何约束 有高手请告知一下
   * ts已经提示是可能获取不到 但是没有想到别的办法,只能告诉ts这里一定存在了
   */
  getRoleKey(key: string): RoleMapItem {
    return this.roleMap.get(key)!;
  }

  validate(key: string): Promise<resultType> {
    return Promise.resolve().then(() => {
      return new Promise((r, s) => {
        const roleItem = this.roleMap.get(key);

        //required字段验证
        if (roleItem?.required && validateMap.required(roleItem.value)) {
          return r({
            is: false,
            msg: `${roleItem.key}为必填项`,
          });
        }

        //验证正则来源
        if (roleItem?.reg && !validateMap.required(roleItem.value)) {
          for (let i = 0, length = roleItem.reg.length; i < length; i++) {
            const regName = roleItem.reg[i].split("|");
            if (!RegExpMap[regName[0]](roleItem.value, regName.slice(1))) {
              return r({
                is: false,
                msg: roleItem.msg![i],
              });
            }
          }
        }
        //自定义正则验证
        if (roleItem?.customReg && !validateMap.required(roleItem.value)) {
          for (let i = 0, length = roleItem.customReg.length; i < length; i++) {
            if (!roleItem.customReg[i].test(roleItem.value)) {
              return r({
                is: false,
                msg: roleItem.msg![i],
              });
            }
          }
        }
        //验证自定义验证方法
        if (roleItem?.custom) {
          const customResult = roleItem.custom(roleItem.value);
          if (customResult instanceof Promise) {
            return customResult.then(
              () => {
                return r({ is: true });
              },
              (msg) => {
                return r({ is: false, msg: msg });
              }
            );
          } else {
            customResult.is ? null : r({ is: false, msg: customResult.msg });
          }
        }
        r({ is: true });
      });
    });
  }

  validateAll(validateList?: Array<string>): Promise<allResult[]> {
    return new Promise((r, s) => {
      const keyList = validateList || Array.from(this.roleMap.keys());
      const errorList: allResult[] = [];
      let forIndex = 0;
      keyList.forEach((validateItemKey, index) => {
        this.validate(validateItemKey).then((result) => {
          forIndex++;
          if (!result.is) {
            errorList.push({
              key: validateItemKey,
              msg: result.msg!,
            });
          }
          if (keyList.length === forIndex) {
            r(errorList);
          }
        });
      });
    });
  }
}
